package Utils.Gameplay;

import gamePlay.ChainOfLevel;
import interfaces.QuestionQueryOperations;
import interfaces.QuestionsGenerator;

import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import main.SharedPref;

import Utils.DebugUtils;

import DBside.NoQueryException;
import DBside.QuestionQueryOperator;
import datatypes.Datatypes;
import datatypes.Datatypes.tableType;

/**
 * qGenerator is in charge of generating a randomized question in respectively
 * to the question context
 * 
 * @author Micha and Yosi
 * 
 */
public class qGenerator implements QuestionsGenerator {

	private QuestionQueryOperations queryOperator;
	private boolean qGeneratorDebug = false;
	private static int ChainLength = SharedPref.chainLengthForLongMod;
	private static tableType lastContextType;
	private Statement stm;
	private boolean firstInChain = true;

	public qGenerator(QuestionQueryOperations queryOperator) {
		this.setQueryOperator(queryOperator);
	}

	public List<Question> genererateQuestion(QuestionContext qContext) {
		switch (qContext.getQType()) {
		case Chain: {
			firstInChain = true;
			return createChainQuestion(qContext);
		}

		}
		return null;
	}

	private List<Question> createChainQuestion(QuestionContext qContext) {

		List<Question> questions = new ArrayList<Question>();
		String contextID = null;
		lastContextType = null;

		// int ChainLength = qContext.getChainLimit();
		QueryResponse qResponse;

		tableType resultType = Datatypes
				.getRandomTypeFrom(tableType.filmActor, tableType.filmDirector,
						tableType.tvActor, tableType.tvProgramCreator,
						tableType.film, tableType.tvProgram);

		int counter = 0;
		boolean qExists = true;
		Question question = null;
		if (qGeneratorDebug) {
			DebugUtils.initTimer();
			DebugUtils.startTimer();
		}
		while (ChainLength > 0) {

			question = null;
			qResponse = createRandomQuestion(contextID, resultType, false);

			while (qResponse == null) {
				qResponse = recoverQuery();
			}

			question = new Question(qResponse);

			while (questionAlreadyExists(question, questions)) {

				qResponse = null;

				while (qResponse == null) {
					qResponse = recoverQuery();
				}

				question = new Question(qResponse);

				if (qGeneratorDebug)
					System.out.println("checking if question already exists");
			}

			// add question to list
			questions.add(question);

			/* ---- prepare data for next question ---- */

			// get correct answer
			Answer rightAnswer = qResponse.getRightAnswer();

			// get result type from response
			resultType = qResponse.getResultType();

			// init last context type for next question
			lastContextType = qResponse.getContextType();

			// get correct answer database id to be used by the next question
			contextID = rightAnswer.getTypeId();

			// just for debug
			String qText = questions.get(counter++).toString();

			if (qGeneratorDebug) {
				System.out.println("\n---------- question # " + counter
						+ "------------");
				System.out.println(qText);
				System.out
						.println("-------------------------------------------------\n");
			}

			ChainLength--;
		}

		if (qGeneratorDebug) {
			System.out.println("chain generation execution time: \n"
					+ DebugUtils.TimeToString(DebugUtils.stopTimer()));
		}

		return questions;

	}

	private boolean questionAlreadyExists(Question question,
			List<Question> questions) {

		// this is just to keep the loop running
		if (question == null || questions == null) {
			return true;
		}

		for (Question questionInList : questions) {
			if (questionInList.getQuestionText().equals(
					question.getQuestionText())) {

				if (qGeneratorDebug) {
					System.out
							.println("found matching question, looking for a new one..");
				}

				return true;
			}
		}

		return false;
	}

	private QueryResponse recoverQuery() {
		tableType resultType = Datatypes
				.getRandomTypeFrom(tableType.filmActor, tableType.filmDirector,
						tableType.tvActor, tableType.tvProgramCreator,
						tableType.film, tableType.tvProgram);

		// in case response is null (could'nt generate a question from
		// the current context)
		return createRandomQuestion(null, resultType, false);
	}

	private QueryResponse createRandomQuestion(String contextID,
			tableType contextType, boolean recoveryMode) {

		tableType randConnection = null;
		QueryContext queryContext = null;

		if (!recoveryMode) {
			if (contextType == null) {
				// contextType = FBdatatypes.getRandomType();
			}

			randConnection = contextType.getRandomConnection(lastContextType);
			if (randConnection == null) {

				if (qGeneratorDebug)
					System.out.println("null connection for "
							+ contextType.name());
				return null;
			}
			queryContext = new QueryContext(contextID, contextType,
					randConnection);

		} else {
			System.out.println("context " + contextType);
			System.out.println("connector " + randConnection);
			queryContext = executeRecoveryMode(contextID, contextType);
		}

		QueryResponse res = null;
		try {
			res = queryOperator.executeQuestionQuery(queryContext, stm,
					firstInChain);
			firstInChain = false;
		} catch (NoQueryException e) {

			if (qGeneratorDebug) {
				System.out.println("no query found for <" + contextID
						+ " of context type "
						+ queryContext.getContext().getTypeRep() + "> !!");
			}
			return null;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return res;

	}

	private QueryContext executeRecoveryMode(String contextID,
			tableType contextType) {

		return null;
	}

	public QuestionQueryOperations getQueryOperator() {
		return queryOperator;
	}

	public void setQueryOperator(QuestionQueryOperations queryOperator) {
		this.queryOperator = queryOperator;
	}

	@Override
	public ChainOfLevel getNextLevel(int level, Statement stmt) {
		stm = stmt;
		// System.out.println(level);
		ChainLength = SharedPref.levelModLengths[level - 1];

		List<Question> questions = genererateQuestion(new QuestionContext(
				QuestionContext.Type.Chain));
		return new ChainOfLevel(convertQuestions(questions), level);
	}

	@Override
	public List<datatype.Question> getNextQuestions(Statement stmt) {
		stm = stmt;
		ChainLength = SharedPref.chainLengthForLongMod;
		List<Question> questions = genererateQuestion(new QuestionContext(
				QuestionContext.Type.Chain));
		return convertQuestions(questions);
	}

	private List<datatype.Question> convertQuestions(List<Question> questions) {
		List<datatype.Question> convertedQuestions = new ArrayList<datatype.Question>();
		for (Question question : questions) {
			convertedQuestions.add(new datatype.Question(question));
		}

		return convertedQuestions;
	}

}
